Custom Entra ID Role — EnergyConnect Infrastructure Deployer
Overview
Each EnergyConnect environment uses an Azure DevOps service connection (SC) to deploy infrastructure. These service connections are backed by Entra ID app registrations with federated credentials.
Rather than granting broad built-in roles (such as Directory Writers or Application Administrator) or tenant-wide Graph API permissions (such as Group.ReadWrite.All), each SC gets the least privilege needed:
- A custom Entra ID role —
EnergyConnect Infrastructure Deployer— that can only create (as owner) and read Entra objects. - The Graph application permission
Application.ReadWrite.OwnedBy— updates, credential rotation and app role assignments, limited to app registrations / service principals the SC owns. - Ownership of the Entra objects its environment deploys. Updates of groups rely on ownership alone.
Result: an SC can only change the apps, service principal and groups of its own environment — not other environments, other customers or anything else in the tenant.
Design Principles
- Create as owner, update by ownership. All create permissions are
createAsOwner, so the SC automatically owns what it creates. There are no tenant-wide update permissions in the role. - One role per tenant. The role is defined once per Entra ID tenant and assigned to each environment's SC at directory scope (required for create permissions).
- Only one Graph app permission.
Application.ReadWrite.OwnedByis the only Graph application permission; it is scoped to owned objects.AppRoleAssignment.ReadWrite.All,Group.ReadWrite.Alletc. are not granted.
What the Deployment Does
The infrastructure deployment (Bicep with extension microsoftGraphV1) performs these Entra ID operations:
| Operation | Bicep resource type | Allowed by |
|---|---|---|
| Create / update app registrations (Client.Auth, AdvancedVEE) | Microsoft.Graph/applications | applications/createAsOwner + Application.ReadWrite.OwnedBy |
| Create / update the Client.Auth service principal | Microsoft.Graph/servicePrincipals | servicePrincipals/createAsOwner + Application.ReadWrite.OwnedBy |
Create / update the 9 environment groups (ec-{env}-{customer}-*) | Microsoft.Graph/groups | groups.security/createAsOwner + ownership |
Create the 3 shared customer groups (ec-{customer}-kunde-*) — first environment only | Microsoft.Graph/groups | groups.security/createAsOwner |
| Assign app roles to groups (on the owned Client.Auth service principal) | Microsoft.Graph/appRoleAssignedTo | Ownership + Application.ReadWrite.OwnedBy |
| Rotate the OIDC client secret (pipeline) | Graph addPassword | Application.ReadWrite.OwnedBy |
Permissions
Custom role
| Permission | Why |
|---|---|
applications/createAsOwner | Create app registrations (Client.Auth, AdvancedVEE). The SC becomes owner. |
applications/standard/read | Read app registrations (Graph extension resolves apps by uniqueName). |
servicePrincipals/createAsOwner | Create the enterprise app (service principal). The SC becomes owner. |
servicePrincipals/standard/read | Read service principals, including appRoles for appRoleAssignedTo. |
groups.security/createAsOwner | Create security groups only (not M365 groups). The SC becomes owner. |
groups/standard/read | Read groups (groups.security/standard/read does not exist). |
Deliberately not included (all are tenant-wide): applications/*/update, applications/credentials/update, servicePrincipals/basic/update, groups.security/basic/update, groups.security/members/update, groups.security/owners/update, servicePrincipals/appRoleAssignedTo/update (not needed: owning the Client.Auth service principal is enough to assign its app roles — verified on fm qa, October 2026). With members/update an SC could add itself to any security group in the tenant.
Graph application permission
| Permission | Why |
|---|---|
Application.ReadWrite.OwnedBy | Update owned app registrations / service principals, assign their app roles to groups and create/rotate their client secrets. Also declared under API permissions on the SC's app registration so it is visible as a consented requirement. |
Management
Creating or Updating the Role
# Requires: Privileged Role Administrator or Global Administrator
az login --tenant <tenant-id>
./Infrastructure/Scripts/entra-id/Set-EntraCustomRole.ps1
Viewing Current Permissions
In the Entra ID portal: Roles and administrators → search for "EnergyConnect Infrastructure Deployer" → Permissions tab.
az rest --method GET `
--url 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions' `
--query "value[?displayName=='EnergyConnect Infrastructure Deployer'].rolePermissions[0].allowedResourceActions"